#include <jni.h>
#include <string>
#include <android/log.h>
#include "calculator.h"
#include "util/jni_map_util.h"
#include "util/jni_list_util.h"
#include "util/jni_iterator_util.h"
//#include "util/jni_int_util.h"
//#include "util/jni_float_util.h"
extern "C" {
  #include "include/jni_string_util.h"
}

#include "util/jni_number_util.h"

#define LOG_TAG "JNI_TAG"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

const int ADD = 0x00;
const int MINUS = 0x01;
const int TIMES = 0x02;
const int DIVIDED = 0x03;

/*
 *  1.jni与app互传数组---已完成
 *  2.jni与app互传列表自定义对象---已完成
 *  3.jni调用c++的类方法---已完成
 *  4.多个lib生成使用---已完成
 *  5.android app没有native层 转 native层实现---已完成
 *  6.编译jni代码为so库并在项目中使用--已完成
 *  7.jni中常用方法统一封装---已完成
 * */

/*---------------------ArrayList--------------------------------*/
//jclass createArrayListClass(JNIEnv *env);
//jobject createArrayListInstance(JNIEnv *env);
//
//
//jclass createArrayListClass(JNIEnv *env) {
//    const char *clazz_name = "java/util/ArrayList";
//    jclass class_list = env->FindClass(clazz_name);
//    return class_list;
//}
//
//jobject createArrayListInstance(JNIEnv *env) {
//    jclass class_list = createArrayListClass(env);
//    jmethodID init_list = env->GetMethodID(class_list, "<init>", "()V");
//    jobject obj_list = env->NewObject(class_list, init_list);
//    return obj_list;
//}

/*---------------------基本数据类型 需要装箱--------------------------------*/
/*---int---*/
//jclass createIntegerClass(JNIEnv *env);
//jobject createIntegerInstance(JNIEnv *env);
//jmethodID createIntegerMethod(JNIEnv *env);
//jmethodID createIntegerMethod2(JNIEnv *env);
//
//jclass createIntegerClass(JNIEnv *env) {
//    const char *clazz_name = "java/lang/Integer";
//    jclass class_int = env->FindClass(clazz_name);
//    return class_int;
//}
//
//jobject createIntegerInstance(JNIEnv *env) {
//    jclass class_int = createIntegerClass(env);
//    jmethodID init_int = env->GetMethodID(class_int, "<init>", "(I)V");
//    jobject obj_int = env->NewObject(class_int, init_int);
//    return obj_int;
//}
//
//jmethodID createIntegerMethod(JNIEnv *env) {
//    jclass class_int = createIntegerClass(env);
//    jmethodID method_id = env->GetMethodID(class_int, "intValue", "()I");
//    return method_id;
//}
//
//jmethodID createIntegerMethod2(JNIEnv *env) {
//    jclass class_int = createIntegerClass(env);
//    jmethodID init_int = env->GetMethodID(class_int, "<init>", "(I)V");
//    return init_int;
//}

/*---Float---*/
//jclass createFloatClass(JNIEnv *env);
//jobject createFloatInstance(JNIEnv *env);
//jmethodID createFloatMethod(JNIEnv *env);
//jmethodID createFloatMethod2(JNIEnv *env);
//
//jclass createFloatClass(JNIEnv *env) {
//    const char *clazz_name = "java/lang/Float";
//    jclass class_float = env->FindClass(clazz_name);
//    return class_float;
//}
//
//jobject createFloatInstance(JNIEnv *env) {
//    jclass class_int = createFloatClass(env);
//    jmethodID init_int = env->GetMethodID(class_int, "<init>", "(F)V");
//    jobject obj_int = env->NewObject(class_int, init_int);
//    return obj_int;
//}
//
//jmethodID createFloatMethod(JNIEnv *env) {
//    jclass class_float = createFloatClass(env);
//    jmethodID method_id = env->GetMethodID(class_float, "floatValue", "()F");
//    return method_id;
//}
//
//jmethodID createFloatMethod2(JNIEnv *env) {
//    jclass class_float = createFloatClass(env);
//    jmethodID init_float = env->GetMethodID(class_float, "<init>", "(F)V");
//    return init_float;
//}

extern "C" JNIEXPORT jstring JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_stringFromJNIInSo(
        JNIEnv* env,
        jobject  /*this*/,
        jstring value0,
        jstring value1) {

    JNIDefineUntil defineUtil;
    jstring target = defineUtil.handString(env, value1, value0);
    //std::string hello = "Hello from C++";
    //return env->NewStringUTF(hello.c_str());
    return target;
}

extern "C" JNIEXPORT jint JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_intFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    return 10;
}

extern "C" JNIEXPORT jint JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_calculateFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jint a,
        jint b,
        jint c) {
    jint result = 0;
    switch (c) {
        case ADD: {
            result = a + b;
            break;
        }
        case TIMES: {
            result = a * b;
            break;
        }
        case MINUS: {
            result = a / b;
            break;
        }
        case DIVIDED: {
            result = a - b;
            break;
        }
        default:
            break;
    }
    return result;
}

extern "C" JNIEXPORT jobject JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobject list) {
    JNIListUtil listUtil;
    //JNIIntUtil intUtil;
    JNINumberUtil<int> numberUtil;

    //创建列表对象和方法
    jclass class_list = listUtil.createArrayListClass(env);
    jobject obj_list = listUtil.createArrayListInstance(env, class_list);
    jmethodID add_list = listUtil.createArrayListAddMethod(env, class_list);

    //遍历列表
    jclass origin_clazz = env->GetObjectClass(list);
    jmethodID origin_get = listUtil.createArrayListGetMethod(env, origin_clazz);
    jmethodID origin_size = listUtil.createArrayListSizeMethod(env, origin_clazz);
    jint origin_len = env->CallIntMethod(list, origin_size);

    for (jint i = 0; i < origin_len; ++i) {
        jobject target_obj = env->CallObjectMethod(list, origin_get, i);
        //jint intValue = env->CallIntMethod(target_obj, intUtil.createIntegerIntValueMethod(env));
        jint intValue = env->CallIntMethod(target_obj, numberUtil.createValueMethod(env));
        if (intValue % 2 == 0) {
            env->CallBooleanMethod(obj_list, add_list, target_obj);
        }
    }

    return obj_list;
}

extern "C" JNIEXPORT jobject JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterMapFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobject map) {
    JNIMapUtil util;
    JNIIteratorUtil iteratorUtil;
    //JNIFloatUtil floatUtil;
    JNINumberUtil<float> floatUtil;

    //遍历Map获取元素
    /*1.获取迭代器类及相关方法*/
    jmethodID method_iterator_hasNext = iteratorUtil.createIteratorHasNextMethod(env);
    jmethodID method_iterator_next = iteratorUtil.createIteratorNextMethod(env);
    /*2.获取Map.Entry相关类及方法*/
    jmethodID method_getKey = util.createHashMapGetKeyMethod(env);
    jmethodID method_getValue = util.createHashMapGetValueMethod(env);
    /*3.迭代器对象获取*/
    jclass class_hash_map = util.createHashMapClass(env);
    jmethodID method_entrySet = env->GetMethodID(class_hash_map, "entrySet", "()Ljava/util/Set;");
    jobject entrySetObject = env->CallObjectMethod(map, method_entrySet);

    jclass class_set = env->FindClass("java/util/Set");
    jmethodID method_iterator = env->GetMethodID(class_set, "iterator", "()Ljava/util/Iterator;");
    jobject iterator_object = env->CallObjectMethod(entrySetObject, method_iterator);

    while (env->CallBooleanMethod(iterator_object, method_iterator_hasNext)) {
        jobject entry_object = env->CallObjectMethod(iterator_object, method_iterator_next);
        auto map_key = (jstring) env->CallObjectMethod(entry_object, method_getKey);
        auto map_value = (jstring) env->CallObjectMethod(entry_object, method_getValue);
        const char* new_key = env->GetStringUTFChars(map_key,NULL);
        const char* new_value = env->GetStringUTFChars(map_value, NULL);
        LOG_D("%s,%s", new_key, new_value);
    }

    /*========================================================================*/
    //创建HashMap对象和put方法
    jclass return_class_hash_map = util.createHashMapClass(env);
    jobject object_hash_map = util.createHashMapInstance(env);
    jmethodID method_put = util.createHashMapPutMethod(env, return_class_hash_map);

    /*1.容器存储String值返回给app*/
    //jobject key = env->NewStringUTF("key");
    //jobject value = env->NewStringUTF("value");

    /*2.容器存储基本数据类型值[int short byte long double char boolean float](需装箱)返回给app*/
    //jobject key = env->NewObject(createIntegerClass(env), createIntegerMethod2(env), 10);
    //jobject value = env->NewObject(createIntegerClass(env), createIntegerMethod2(env), 5);

    jobject key = env->NewObject(floatUtil.createClass(env), floatUtil.createConstructor(env), 10.0F);
    jobject value = env->NewObject(floatUtil.createClass(env), floatUtil.createConstructor(env), 5.0F);

    env->CallObjectMethod(object_hash_map, method_put, key, value);

    /*回收创建的对象*/
    //env->DeleteLocalRef(key);
    //env->DeleteLocalRef(value);
    //env->DeleteLocalRef(object_hash_map);
    //env->DeleteLocalRef(class_hash_map);
    return object_hash_map;
}


extern "C" JNIEXPORT jintArray JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterArrayFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jintArray array) {
    //遍历数组
    jsize length = env->GetArrayLength(array);
    //通过指针遍历数组元素
    jint *value = env->GetIntArrayElements(array, NULL);
    LOG_D("%s", "=======================================================");
    for (jsize i = 0; i < length; ++i) {
        LOG_D("%d", *(value + i));
    }

    //创建返回数组
    const jint size = 10;
    jintArray intArray = env->NewIntArray(size);
    jint buf[size] = {0};
    for (jint i = 0; i < size; ++i) {
        buf[i] = i * i;
    }
    env->SetIntArrayRegion(intArray, 0, size, buf);
    return intArray;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterArrayObjectFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobjectArray array
        ) {
    //JNIIntUtil intUtil;
    JNINumberUtil<int> intUtil;

    //遍历数组
    jsize length = env->GetArrayLength(array);
    for (jsize i = 0; i < length; ++i) {
        auto dataItem = (jstring) env->GetObjectArrayElement(array, i);
        LOG_D("%s",env->GetStringUTFChars(dataItem,NULL));
    }

    //创建返回数组
    const jsize size = 10;
    jclass elementClass = intUtil.createClass(env);
    jobject initElement = env->NewObject(intUtil.createClass(env), intUtil.createConstructor(env), 0);
    jobjectArray objArray = env->NewObjectArray(size, elementClass, initElement);
    for (jsize i = 0; i < size; ++i) {
        jobject item = env->NewObject(intUtil.createClass(env), intUtil.createConstructor(env), i + i);
        env->SetObjectArrayElement(objArray, i, item);
    }
    return objArray;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterArrayDefineObjectFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobjectArray array
) {
    //JNIIntUtil intUtil;
    JNINumberUtil<int> intUtil;
    //遍历数组
    jsize length = env->GetArrayLength(array);
    for (jsize i = 0; i < length; ++i) {
        jobject item_obj = env->GetObjectArrayElement(array, i);

        jclass item_class = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");

        jfieldID first_field = env->GetFieldID(item_class,"data","I");
        jint firstValue = env->GetIntField(item_obj, first_field);

        jfieldID second_field = env->GetFieldID(item_class,"content", "Ljava/lang/String;");
        auto secondValue = (jstring)env->GetObjectField(item_obj, second_field);

        LOG_D("%d,%s",firstValue,env->GetStringUTFChars(secondValue,JNI_FALSE));
    }

    //调用自定义cpp中的功能
    Calculator calculator;
    jint value_from_outer = calculator.getArea(10, 5);
    LOG_D("value_from_outer==%d",value_from_outer);

    //创建返回数组
    const jsize size = 15;

    jclass elementClass = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");
    jmethodID methodId = env->GetMethodID(elementClass, "<init>", "(ILjava/lang/String;)V");

    jobject firstValue = env->NewObject(intUtil.createClass(env), intUtil.createConstructor(env), 0);
    jobject secondValue = env->NewStringUTF("学习JNI");
    jobject initElement = env->NewObject(elementClass, methodId, firstValue, secondValue);
    jobjectArray objArray = env->NewObjectArray(size, elementClass, initElement);

    for (jsize i = 0; i < size; ++i) {
        //jobject value0 = env->NewObject(createIntegerClass(env), createIntegerMethod2(env), i);
        jint value0 = i;
        //LOG_D("%d",env->CallIntMethod(value0, createIntegerMethod(env)));


        jclass string_class = env->FindClass("java/lang/String");
        jmethodID value_of_method = env->GetStaticMethodID(string_class, "valueOf","(I)Ljava/lang/String;");
        jmethodID concat_method = env->GetMethodID(string_class, "concat","(Ljava/lang/String;)Ljava/lang/String;");
        auto content0 = (jstring) env->CallStaticObjectMethod(string_class, value_of_method, value0);
        jstring content1 = env->NewStringUTF("学习JNI--");
        auto value1 = (jstring) env->CallObjectMethod(content1, concat_method, content0);
        //LOG_D("%s",env->GetStringUTFChars(value2,JNI_FALSE));
        jobject item = env->NewObject(elementClass, methodId, value0, value1);
        env->SetObjectArrayElement(objArray, i, item);
    }

    return objArray;
}

extern "C" JNIEXPORT jobject JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterListDefineObjectFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobject object
) {
    JNIListUtil listUtil;
    JNIIteratorUtil iteratorUtil;

    //遍历对象列表---索引
//    jclass origin_class = env->GetObjectClass(object);
//    jmethodID method_size = listUtil.createArrayListSizeMethod(env, origin_class);
//    jint itemCount = env->CallIntMethod(object, method_size);
//    jmethodID method_get = listUtil.createArrayListGetMethod(env, origin_class);
//    for (jint i = 0; i < itemCount; ++i) {
//        jobject item_obj = env->CallObjectMethod(object, method_get, i);
//
//        jclass item_class = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");
//
//        jfieldID first_field = env->GetFieldID(item_class,"data","I");
//        jint firstValue = env->GetIntField(item_obj, first_field);
//
//        jfieldID second_field = env->GetFieldID(item_class,"content", "Ljava/lang/String;");
//        auto secondValue = (jstring)env->GetObjectField(item_obj, second_field);
//
//        LOG_D("%d,%s",firstValue,env->GetStringUTFChars(secondValue,JNI_FALSE));
//    }

    //遍历对象列表---迭代器
    /*1.获取迭代器类及相关方法*/
    jmethodID method_iterator_hasNext = iteratorUtil.createIteratorHasNextMethod(env);
    jmethodID method_iterator_next = iteratorUtil.createIteratorNextMethod(env);
    /*2.迭代器对象获取*/
    jclass class_list = env->FindClass("java/util/List");
    jmethodID method_iterator = env->GetMethodID(class_list, "iterator", "()Ljava/util/Iterator;");
    jobject iterator_object = env->CallObjectMethod(object, method_iterator);
    while (env->CallBooleanMethod(iterator_object,method_iterator_hasNext)) {
        jobject item_obj = env->CallObjectMethod(iterator_object, method_iterator_next);

        jclass item_class = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");

        jfieldID first_field = env->GetFieldID(item_class,"data","I");
        jint firstValue = env->GetIntField(item_obj, first_field);

        jfieldID second_field = env->GetFieldID(item_class,"content", "Ljava/lang/String;");
        auto secondValue = (jstring)env->GetObjectField(item_obj, second_field);

        LOG_D("%d,%s",firstValue,env->GetStringUTFChars(secondValue,JNI_FALSE));
    }


    //返回对象列表
    jsize items = 5;
    jclass return_class_list = listUtil.createArrayListClass(env);
    jobject return_obj = listUtil.createArrayListInstance(env, return_class_list);
    jmethodID add_list = listUtil.createArrayListAddMethod(env, return_class_list);

    for (jsize i = 0; i < items; ++i) {
        jclass elementClass = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");
        jmethodID methodId = env->GetMethodID(elementClass, "<init>", "(ILjava/lang/String;)V");

        //jobject value0 = env->NewObject(createIntegerClass(env), createIntegerMethod2(env), i);
        jint value0 = i;
        //LOG_D("%d",env->CallIntMethod(value0, createIntegerMethod(env)));

        jclass string_class = env->FindClass("java/lang/String");
        jmethodID value_of_method = env->GetStaticMethodID(string_class, "valueOf","(I)Ljava/lang/String;");
        jmethodID concat_method = env->GetMethodID(string_class, "concat","(Ljava/lang/String;)Ljava/lang/String;");
        auto content0 = (jstring) env->CallStaticObjectMethod(string_class, value_of_method, value0);
        jstring content1 = env->NewStringUTF("学习JNI--");
        auto value1 = (jstring) env->CallObjectMethod(content1, concat_method, content0);
        //LOG_D("%s",env->GetStringUTFChars(value2,JNI_FALSE));
        jobject item = env->NewObject(elementClass, methodId, value0, value1);

        env->CallBooleanMethod(return_obj, add_list, item);
    }
    return return_obj;
}

extern "C" JNIEXPORT jobject JNICALL Java_com_hengfoxwolf_jnidemo_MainActivity_filterMapDefineObjectFromJNI(
        JNIEnv *env,
        jobject /* this */,
        jobject object
) {
    JNIMapUtil util;
    JNIIteratorUtil iteratorUtil;
    //JNIIntUtil intUtil;
    JNINumberUtil<int> intUtil;

    //遍历Map
    /*1.获取迭代器类及相关方法*/
    jmethodID method_iterator_hasNext = iteratorUtil.createIteratorHasNextMethod(env);
    jmethodID method_iterator_next = iteratorUtil.createIteratorNextMethod(env);
    /*2.获取Map.Entry相关类及方法*/
    jmethodID method_getKey = util.createHashMapGetKeyMethod(env);
    jmethodID method_getValue = util.createHashMapGetValueMethod(env);
    /*3.迭代器对象获取*/
    jclass class_hash_map = util.createHashMapClass(env);
    jmethodID method_entrySet = env->GetMethodID(class_hash_map, "entrySet", "()Ljava/util/Set;");
    jobject entrySetObject = env->CallObjectMethod(object, method_entrySet);

    jclass class_set = env->FindClass("java/util/Set");
    jmethodID method_iterator = env->GetMethodID(class_set, "iterator", "()Ljava/util/Iterator;");
    jobject iterator_object = env->CallObjectMethod(entrySetObject, method_iterator);

    while (env->CallBooleanMethod(iterator_object, method_iterator_hasNext)) {
        jobject entry_object = env->CallObjectMethod(iterator_object, method_iterator_next);

        jobject map_key = env->CallObjectMethod(entry_object, method_getKey);
        jint map_key_00 = env->CallIntMethod(map_key, intUtil.createValueMethod(env));

        jobject map_value = env->CallObjectMethod(entry_object, method_getValue);
        jclass item_class = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");

        jfieldID first_field = env->GetFieldID(item_class,"data","I");
        jint firstValue = env->GetIntField(map_value, first_field);

        jfieldID second_field = env->GetFieldID(item_class,"content", "Ljava/lang/String;");
        auto secondValue = (jstring)env->GetObjectField(map_value, second_field);

        //const char* new_key = env->GetStringUTFChars(map_key,NULL);
        //const char* new_value = env->GetStringUTFChars(map_value, NULL);
        LOG_D("%d,%d,%s", map_key_00, firstValue,env->GetStringUTFChars(secondValue,JNI_FALSE));
    }

    //调用自定义cpp中的功能
    Calculator calculator;
    jint value_from_outer = calculator.doSimpleCalculate(10, 5, '/');
    LOG_D("value_from_outer==%d",value_from_outer);

    //返回--创建HashMap对象和put方法
    jsize count = 5;
    jclass return_class_hash_map = util.createHashMapClass(env);
    jobject object_hash_map = util.createHashMapInstance(env);

    jmethodID method_put = util.createHashMapPutMethod(env, return_class_hash_map);
    for (jint i = 0; i < count; ++i) {
        jobject key = env->NewObject(intUtil.createClass(env), intUtil.createConstructor(env), i);

        jclass elementClass = env->FindClass("com/hengfoxwolf/jnidemo/bean/LearnThing");
        jmethodID methodId = env->GetMethodID(elementClass, "<init>", "(ILjava/lang/String;)V");

        //jobject value0 = env->NewObject(createIntegerClass(env), createIntegerMethod2(env), i);
        jint value0 = i;
        //LOG_D("%d",env->CallIntMethod(value0, createIntegerMethod(env)));

        jclass string_class = env->FindClass("java/lang/String");
        jmethodID value_of_method = env->GetStaticMethodID(string_class, "valueOf","(I)Ljava/lang/String;");
        jmethodID concat_method = env->GetMethodID(string_class, "concat","(Ljava/lang/String;)Ljava/lang/String;");
        auto content0 = (jstring) env->CallStaticObjectMethod(string_class, value_of_method, value0);
        jstring content1 = env->NewStringUTF("CallObjectMethod--");
        auto value1 = (jstring) env->CallObjectMethod(content1, concat_method, content0);
        //LOG_D("%s",env->GetStringUTFChars(value2,JNI_FALSE));
        jobject value = env->NewObject(elementClass, methodId, value0, value1);

        env->CallObjectMethod(object_hash_map, method_put, key, value);
    }
    return object_hash_map;
}